﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Kit3D.Windows.Controls;
using Kit3D.Windows.Media.Media3D;
using Kit3D.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Windows.Media.Effects;
using ShaderEffectLibrary;
using System.IO;
using System.Runtime.InteropServices;

namespace Cloth3D.Classes
{

    class thing3d
    {
        public Model3D model;
        public string name;

        public thing3d(string new_name, Model3D new_model)
        {
            model = new Model3DGroup();
            ((Model3DGroup)model).Children.Add(new_model);
            //model = new_model;
            name = new_name;
        }

        public void input()
        {
        }


        public void update()
        {
        }


       
    }

    public partial class Cloth : UserControl
    {
        public event EventHandler<EventArgs> IsOperative = null;
        private int meshesLoaded = 0;


        private Viewport3D viewport;
        private Matrix3D viewMatrix;
        private Matrix3D projectionMatrix;
        private Vector3D viewMatrixLookAt = new Vector3D();
        private Vector3D viewMatrixUpDirection = new Vector3D();
        private Point3D viewMatrixPosition = new Point3D();
        private MatrixCamera camera;


        static bool update_cloth = true;
        static bool a = false, b = false, c = false, d = false;
        static node_grid my_node_grid;
        static box my_box;
        static box floor;

        static List<thing3d> things3d = new List<thing3d>();
        private DispatcherTimer timer = new DispatcherTimer();

        private int oldTime = int.MinValue;
        private int actualTime = int.MinValue;

        public Cloth()
        {
            InitializeComponent();

           this.SetScene();

           oldTime = Environment.TickCount;
           actualTime = Environment.TickCount;

           Kit3D.Windows.Media.CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
           
        }

        
        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            actualTime = Environment.TickCount;
            double delta_t = (actualTime - oldTime)/1000.0;
            oldTime = actualTime;

            Vector3D sum_movement = new Vector3D();

            Vector3D forward_vec = new Vector3D(viewMatrix.M13, viewMatrix.M23, viewMatrix.M33);
            forward_vec.Normalize();

            Vector3D right_vec = new Vector3D(viewMatrix.M11, viewMatrix.M21, viewMatrix.M31);


            double linear_speed = 3000 * delta_t;



            if (sum_movement.Length > 0)
            {
                sum_movement.Normalize();
                Matrix3D movement_mat = new TranslateTransform3D(sum_movement * linear_speed).Value;

                //Transform3D.CreateTranslation(sum_movement * linear_speed).Value; // LINEAR SPEED
                viewMatrix = movement_mat * viewMatrix;
            }


            my_node_grid.update_brush_contents();


            if (update_cloth)
            {

                for (int m = 0; m < my_node_grid.x_node_count; m++)
                {	// gravity
                    for (int n = 0; n < my_node_grid.y_node_count; n++)
                    {
                        if (!my_node_grid.grid[m, m].IsFixed)
                            my_node_grid.grid[m, n].total_force.Y += -200000;
                    }
                }


                //if (d)
                //{
                //    my_node_grid.grid[19, 19].position = new Point3D(500, 600, 0);
                //    my_node_grid.grid[19, 19].velocity = new Vector3D(0, 0, 0);
                //    my_node_grid.grid[19, 19].total_force = new Vector3D(0, 0, 0);
                //}

                //if (c)
                //{
                //    my_node_grid.grid[13, 19].position = new Point3D(333, 600, 0);
                //    my_node_grid.grid[13, 19].velocity = new Vector3D(0, 0, 0);
                //    my_node_grid.grid[13, 19].total_force = new Vector3D(0, 0, 0);
                //}

                //if (b)
                //{
                //    my_node_grid.grid[6, 19].position = new Point3D(166, 600, 0);
                //    my_node_grid.grid[6, 19].velocity = new Vector3D(0, 0, 0);
                //    my_node_grid.grid[6, 19].total_force = new Vector3D(0, 0, 0);
                //}


                //if (a)
                //{
                //    my_node_grid.grid[0, 19].position = new Point3D(0, 600, 0);
                //    my_node_grid.grid[0, 19].velocity = new Vector3D(0, 0, 0);
                //    my_node_grid.grid[0, 19].total_force = new Vector3D(0, 0, 0);
                //}



                my_node_grid.update_physics_generate_next_positions();


                // COLLISION DETECTION

                my_node_grid.check_box_collision(my_box);
               
                //my_node_grid.check_sphere_collision(my_sphere);


                my_node_grid.commit_new_positions();

            }


        }




        public void ResetScene()
        {
            viewport.Children.Clear();

            things3d.Clear();
            
            viewport.SizeChanged -= viewport_SizeChanged;
            this.SetScene();
        }



        private void SetScene()
        {
            this.LayoutRoot.Children.Remove(viewport);
            viewport = null;

            double pictureSize = 500;
            double mass = 1.0;
            int rows = 20;
            int cols = 20;

            // Create Viewport3D as content of window.
            viewport = new Viewport3D();
            viewport.HorizontalAlignment = HorizontalAlignment.Stretch;
            viewport.VerticalAlignment = VerticalAlignment.Stretch;
            viewport.SizeChanged += new SizeChangedEventHandler(viewport_SizeChanged);


            MatrixCamera matrix_camera = new MatrixCamera();
            this.LayoutRoot.Children.Add(viewport);
            my_node_grid = new node_grid(new Point3D(0, 500, 0), pictureSize, pictureSize, rows, cols, mass, 5000000, 1500, 2500000, 1000, 30000, 1000);
            things3d.Add(new thing3d("mesh", my_node_grid.model));

           

            my_box = new box(new Point3D(0, 100, 0), 300, 300, 300, 0.3, 0.01, Colors.Transparent);
            things3d.Add(new thing3d("box1", my_box.model));

            
            

            for (int n = 0; n < things3d.Count; n++)
            {
                ModelVisual3D mv3d = new ModelVisual3D();
                mv3d.Content = things3d[n].model;
                viewport.Children.Add(mv3d);
            }
        }

       

       

        internal double DegreesToRadians(double angleInDegrees)
        {
            return angleInDegrees * ((2 * Math.PI) / 360.0);
        }

        internal double RadiansToDegrees(double angleInRadians)
        {
            return angleInRadians * (360.0/(2 * Math.PI));
        }

      

        #region View / Projection matrices


        /// <summary>
        /// Called when the viewport change ssize, if this happens we need to recompute the view/projection matrices
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void viewport_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            viewMatrixLookAt.X = -1500;
            viewMatrixLookAt.Y = -1500;
            viewMatrixLookAt.Z = -1500;

            viewMatrixUpDirection.X = 0;
            viewMatrixUpDirection.Y = 1;
            viewMatrixUpDirection.Z = 0;

            viewMatrixPosition.X = 1500;
            viewMatrixPosition.Y = 1500;
            viewMatrixPosition.Z = 1500;

            viewMatrix = ViewMatrix(viewMatrixLookAt, viewMatrixUpDirection, viewMatrixPosition);
            projectionMatrix = ProjectionMatrix(this.viewport.ActualWidth / this.viewport.ActualHeight, 45, double.PositiveInfinity, 0.125);

            //Update camera
            camera = new MatrixCamera(viewMatrix, projectionMatrix);
           
            viewport.Camera = camera;
        }

        // <summary>
        /// Calculates the matrix that transforms world coords to view coords
        /// </summary>
        /// <param name="lookDirection"></param>
        /// <param name="upDirection"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        private Matrix3D ViewMatrix(Vector3D lookDirection, Vector3D upDirection, Point3D position)
        {
            Vector3D cameraZAxis = -lookDirection;
            cameraZAxis.Normalize();

            Vector3D cameraXAxis = Vector3D.CrossProduct(upDirection, cameraZAxis);
            cameraXAxis.Normalize();

            Vector3D cameraYAxis = Vector3D.CrossProduct(cameraZAxis, cameraXAxis);

            Vector3D cameraPosition = (Vector3D)position;
            double offsetX = -Vector3D.DotProduct(cameraXAxis, cameraPosition);
            double offsetY = -Vector3D.DotProduct(cameraYAxis, cameraPosition);
            double offsetZ = -Vector3D.DotProduct(cameraZAxis, cameraPosition);

            return new Matrix3D(cameraXAxis.X, cameraYAxis.X, cameraZAxis.X, 0,
                                cameraXAxis.Y, cameraYAxis.Y, cameraZAxis.Y, 0,
                                cameraXAxis.Z, cameraYAxis.Z, cameraZAxis.Z, 0,
                                offsetX, offsetY, offsetZ, 1);
        }

        /// <summary>
        /// Calculates the matrix that transforms view coords in 3D to 2D points on the
        /// projection plane
        /// </summary>
        /// <param name="nearPlaneDistance"></param>
        /// <param name="farPlaneDistance"></param>
        /// <param name="fieldOfView"></param>
        /// <returns></returns>
        internal Matrix3D ProjectionMatrix(double viewportAspectRatio, double fieldOfView, double farPlaneDistance, double nearPlaneDistance)
        {
            //Now we can create the projection matrix
            double aspectRatio = viewportAspectRatio;
            double xScale = 1.0 / System.Math.Tan(System.Math.PI * fieldOfView / 360);
            double yScale = aspectRatio * xScale;
            double zScale = (farPlaneDistance == double.PositiveInfinity) ? -1 : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
            double zOffset = nearPlaneDistance * zScale;

            return new Matrix3D(xScale, 0, 0, 0,
                                0, yScale, 0, 0,
                                0, 0, zScale, -1,
                                0, 0, zOffset, 0);
        }

        #endregion






        private static GeometryModel3D generate_floor_mesh(double height)
        {

            const int edge = 2;

            MeshGeometry3D mesh = new MeshGeometry3D();

            for (int n = 0; n < edge; n++)
            { // n -> x
                for (int m = 0; m < edge; m++)
                { // m -> y
                    mesh.Positions.Add(new Point3D(((n / ((double)edge - 1)) - 0.5) * 1000.0, height, ((m / ((double)edge - 1)) - 0.5) * 1000.0));


                    mesh.TextureCoordinates.Add(new Point(n / (double)edge, m / (double)edge));
                }
            }


            for (int n = 0; n < edge - 1; n++)
            {
                for (int m = 0; m < edge - 1; m++)
                {
                    mesh.TriangleIndices.Add(n + (m * edge));
                    mesh.TriangleIndices.Add(n + ((m + 1) * edge));
                    mesh.TriangleIndices.Add((n + 1) + (m * edge));

                    mesh.TriangleIndices.Add((n + 1) + (m * edge));
                    mesh.TriangleIndices.Add(n + ((m + 1) * edge));
                    mesh.TriangleIndices.Add(n + (m * edge));


                    mesh.TriangleIndices.Add(n + ((m + 1) * edge));
                    mesh.TriangleIndices.Add((n + 1) + ((m + 1) * edge));
                    mesh.TriangleIndices.Add((n + 1) + (m * edge));

                    mesh.TriangleIndices.Add((n + 1) + (m * edge));
                    mesh.TriangleIndices.Add((n + 1) + ((m + 1) * edge));
                    mesh.TriangleIndices.Add(n + ((m + 1) * edge));

                }
            }

            GeometryModel3D model = new GeometryModel3D();
            model.Geometry = mesh;
            model.Material = new DiffuseMaterial(new Kit3DBrush(new SolidColorBrush(Colors.White)));
            model.BackMaterial = new DiffuseMaterial(new Kit3DBrush(new SolidColorBrush(Colors.Blue)));
            return model;
        }

      
    }
}
